package podman

import (
	containers_ag "com.yy.api/agent/lib/containers"
	"com.yy.api/common/logger"
	cP "com.yy.api/common/pojo/param/container"
	containerParam "com.yy.api/common/pojo/param/container"
	"context"
	"fmt"
	"github.com/containers/podman/v4/libpod/define"
	"github.com/containers/podman/v4/pkg/bindings"
	"github.com/containers/podman/v4/pkg/bindings/containers"
	"github.com/containers/podman/v4/pkg/bindings/images"
	"github.com/containers/podman/v4/pkg/domain/entities"
	"github.com/containers/podman/v4/pkg/domain/entities/reports"
	"os"
)

type PodmanService interface {
	CreateContainer(param containerParam.CreateContainerParam) (entities.ContainerCreateResponse, error)
	PullImage(rowImage string) error
	StartContainer(ID string) error
	InspectContainer(nameOrID string) (*define.InspectContainerData, error)
	ReleaseContainer(nameOrID string) ([]*reports.RmReport, error)
	HasContainer(nameOrID string) (bool, error)
	ListContainers(param cP.ListContainerParam) ([]*entities.ListContainer, error)
	HealthCheckContainer(nameOrID string) (*define.HealthCheckResults, error)
}

type PodmanServiceImpl struct {
	Conn context.Context
}

var _ PodmanService = &PodmanServiceImpl{}

func NewPodmanService() PodmanService {
	conn, err := getConnection()
	if err != nil {
		logger.Default.Errorf("Fail to initialize connection", err)
		return nil
	}
	return &PodmanServiceImpl{
		Conn: conn,
	}
}

func getConnection() (context.Context, error) {
	sockDir := os.Getenv("XDG_RUNTIME_DIR")
	if sockDir == "" {
		return nil, fmt.Errorf("environment variable XDG_RUNTIME_DIR is not set")
	}

	socketPath := "unix:" + sockDir + "/podman/podman.sock"
	conn, err := bindings.NewConnection(context.Background(), socketPath)
	if err != nil {
		return nil, fmt.Errorf("failed to connect to podman socket: %w", err)
	}
	return conn, nil
}

func (p *PodmanServiceImpl) ReleaseContainer(nameOrID string) ([]*reports.RmReport, error) {
	if err := containers.Kill(p.Conn, nameOrID, nil); err != nil {
		return nil, err
	}
	return containers.Remove(p.Conn, nameOrID, nil)
}
func (p *PodmanServiceImpl) PullImage(rowImage string) error {
	_, err := images.Pull(p.Conn, rowImage, nil)
	if err != nil {
		logger.Default.Errorf("Pull Remote Image Failed", err)
		return err
	}
	return nil
}

func (p *PodmanServiceImpl) ListContainers(param cP.ListContainerParam) ([]*entities.ListContainer, error) {
	ob := containers_ag.NewListOptionBuilder()
	hostPorts := param.HostPorts
	if hostPorts != nil && len(hostPorts) != 0 {
		for _, hostPort := range hostPorts {
			ob.AddHostPort(hostPort)
		}
	}
	status := param.Status
	if status != nil && len(status) != 0 {
		ob.AddStatus(status...)
	}
	userId := param.UserId
	if userId != "" {
		ob.AddUserId(userId)
	}
	displayId := param.DisplayId
	if displayId != "" {
		ob.AddDisplayId(displayId)
	}
	containerId := param.ContainerID
	if containerId != "" {
		ob.AddContainerId(containerId)
	}
	gpuIds := param.GpuIds
	if gpuIds != nil && len(gpuIds) != 0 {
		for _, gpuId := range gpuIds {
			ob.AddGpuId(gpuId)
		}
	}
	list, err := ob.List(p.Conn)
	if err != nil {
		return nil, err
	}
	ptrList := make([]*entities.ListContainer, len(list))
	for i, v := range list {
		ptrList[i] = &v
	}
	return ptrList, nil
}
func (p *PodmanServiceImpl) StartContainer(ID string) error {
	return containers.Start(p.Conn, ID, nil)
}

func (p *PodmanServiceImpl) InspectContainer(nameOrID string) (*define.InspectContainerData, error) {
	result, err := containers.Inspect(p.Conn, nameOrID, new(containers.InspectOptions).WithSize(true))
	if err != nil {
		return nil, err
	}
	return result, nil
}

func (p *PodmanServiceImpl) CreateContainer(param containerParam.CreateContainerParam) (entities.ContainerCreateResponse, error) {
	rowImage := param.GetRowImage()
	portMappings := param.GetPortMappings()
	cs := containers_ag.NewCreateSpec(rowImage)
	if portMappings != nil && len(portMappings) != 0 {
		for _, portMapping := range portMappings {
			cs.AddPortMapping(portMapping.HostPort, portMapping.ContainerPort)
		}
	} else {
		logger.Default.Infof("No Port Mappings:%v", portMappings)
	}
	gpuIds := param.GetGpuIds()
	if len(gpuIds) > 0 {
		for _, gpuId := range gpuIds {
			cs.AddNvidiaGpuDevice(gpuId)
		}
	}
	cs.AddNvidiaBasicEnv()
	cs.AddLabel("user_id", param.UserId)
	cs.AddLabel("display_id", param.DisplayId)
	return cs.CreateWithSpec(p.Conn)
}

func (p *PodmanServiceImpl) HealthCheckContainer(nameOrID string) (*define.HealthCheckResults, error) {
	return containers.RunHealthCheck(p.Conn, nameOrID, nil)
}

func (p *PodmanServiceImpl) HasContainer(nameOrID string) (bool, error) {
	return containers.Exists(p.Conn, nameOrID, nil)
}
